package cc.aiee.wxpay.api;

import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.nio.charset.StandardCharsets;
import java.security.GeneralSecurityException;
import java.util.Map;

import org.apache.http.ParseException;
import org.apache.http.client.ClientProtocolException;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.util.EntityUtils;

import com.alibaba.fastjson.JSONObject;

import cc.aiee.core0.bean.J;
import cc.aiee.core0.bean.R;
import cc.aiee.core0.enums.MsgCodeEnum;
import cc.aiee.core0.enums.StatusEnum;
import cc.aiee.core0.utils.ValidUtil;
import cc.aiee.wxpay.config.WxPayScoreConfig;
import cc.aiee.wxpay.config.WxPayScoreConfigKit;
import cc.aiee.wxpay.core.pojo.dto.score.WxPayScoreCompleteDTO;
import cc.aiee.wxpay.core.pojo.dto.score.WxPayScoreCreateMinAppDTO;
import cc.aiee.wxpay.core.pojo.dto.score.WxPayScoreRefundDTO;
import cc.aiee.wxpay.core.pojo.dto.score.notify.WxPayNotifyResource;
import cc.aiee.wxpay.core.pojo.dto.score.notify.WxPayScoreNotifyCiphertextDTO;
import cc.aiee.wxpay.core.pojo.dto.score.notify.WxPayScoreNotifyPlainTextDTO;
import cc.aiee.wxpay.core.tool.AesUtil;
import cc.aiee.wxpay.core.tool.WxPayScoreTool;
import cc.aiee.wxpay.model.score.complete.WxPayCompleteOrderModel;
import cc.aiee.wxpay.model.score.create.WxPayCreateServiceOrderModel;
import cc.aiee.wxpay.model.score.refund.WxPayRefundModel;
import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.util.CharsetUtil;
import cn.hutool.core.util.StrUtil;
import lombok.extern.slf4j.Slf4j;

@Slf4j
public class WxPayScoreApi {

	public R<WxPayScoreCreateMinAppDTO> createServiceOrderMinApp(WxPayCreateServiceOrderModel model,
			WxPayScoreConfig config) {
		R<String> result = createServiceOrder(model, config);
		if (result.fail()) {
			return new R<>(result);
		}
		String body = result.getObj();
		JSONObject jso = JSONObject.parseObject(body);
		Map<String, Object> minAppCreateDto = WxPayScoreTool.toMinAppCreateDto(jso, config.getApiSecret());
		WxPayScoreCreateMinAppDTO mapToBean = BeanUtil.mapToBean(minAppCreateDto, WxPayScoreCreateMinAppDTO.class,
				false);
		mapToBean.setAppid(model.getAppid());
		mapToBean.setPackage2(String.valueOf(minAppCreateDto.get("package")));
		return new R<>(MsgCodeEnum.SUCCESS, mapToBean);
	}

	public R<String> createServiceOrder(WxPayCreateServiceOrderModel model, WxPayScoreConfig config) {
		ValidUtil.asserts(model);
		if (StrUtil.isBlank(model.getService_id())) {
			model.setService_id(config.getServiceId());
		}
		if (StrUtil.isBlank(model.getNotify_url())) {
			model.setNotify_url(config.getNotifyUrl());
		}
		try {
			// 请求URL
			HttpPost httpPost = new HttpPost("https://api.mch.weixin.qq.com/v3/payscore/serviceorder");
			String reqdata = model.toJson();

			if (log.isDebugEnabled()) {
				log.debug("创建微信支付分请求内容:{}", reqdata);
			}
			StringEntity entity = new StringEntity(reqdata, CharsetUtil.CHARSET_UTF_8);
			entity.setContentType("application/json");
			httpPost.setEntity(entity);
			httpPost.setHeader("Accept", "application/json");

			CloseableHttpClient httpClient = WxPayScoreConfigKit.getHttpClientCache(config);
			// 完成签名并执行请求
			CloseableHttpResponse response = httpClient.execute(httpPost);
			int statusCode = response.getStatusLine().getStatusCode();
			if (statusCode == 200) { // 处理成功
				String body = EntityUtils.toString(response.getEntity());
				return new R<>(MsgCodeEnum.SUCCESS, body);
//				System.out.println("success,return body = " + body);
			} else if (statusCode == 204) { // 处理成功，无返回Body
				System.out.println("success");
			} else {
//				System.out.println("failed,resp code = " + statusCode + ",return body = "
//						+ EntityUtils.toString(response.getEntity()));
//			    throw new IOException("request failed");
				return new R<>(String.valueOf(statusCode), EntityUtils.toString(response.getEntity()), StatusEnum.FAIL);
			}
		} catch (UnsupportedEncodingException e) {
			e.printStackTrace();
		} catch (ClientProtocolException e) {
			e.printStackTrace();
		} catch (ParseException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		}
		return new R<>(MsgCodeEnum.F_EXCEPTION_2);
	}

	public R<WxPayScoreCompleteDTO> completeServiceOrder(WxPayCompleteOrderModel model, WxPayScoreConfig config) {
		ValidUtil.asserts(model);
		if (StrUtil.isBlank(model.getService_id())) {
			model.setService_id(config.getServiceId());
		}
		if (StrUtil.isBlank(model.getAppid())) {
			model.setAppid(config.getAppId());
		}
		String url = StrUtil.format("https://api.mch.weixin.qq.com/v3/payscore/serviceorder/{}/complete",
				model.getOut_order_no());
		try {
			// 请求URL
			HttpPost httpPost = new HttpPost(url);

			String reqdata = model.toJson();

			String debugmsg = StrUtil.format("WxPayScoreComplete支付分完结请求参数:{}", reqdata);
			System.out.println(debugmsg);
			if (log.isDebugEnabled()) {
				log.debug(debugmsg);
			}
			StringEntity entity = new StringEntity(reqdata, CharsetUtil.CHARSET_UTF_8);

			entity.setContentType("application/json");
			httpPost.setEntity(entity);
			httpPost.setHeader("Accept", "application/json");

			CloseableHttpClient httpClient = WxPayScoreConfigKit.getHttpClientCache(config);
			// 完成签名并执行请求
			CloseableHttpResponse response = httpClient.execute(httpPost);

			int statusCode = response.getStatusLine().getStatusCode();
			if (statusCode == 200) { // 处理成功
//				System.out.println("success,return body = " + EntityUtils.toString(response.getEntity()));
				String resultStr = EntityUtils.toString(response.getEntity());
				WxPayScoreCompleteDTO parseObject = JSONObject.parseObject(resultStr, WxPayScoreCompleteDTO.class);
				return new R<>(MsgCodeEnum.SUCCESS, parseObject);
			} else if (statusCode == 204) { // 处理成功，无返回Body
				// System.out.println("success");
				log.info("WxPayScoreComplete微信支付分完结无body返回");
			} else {
//				System.out.println("failed,resp code = " + statusCode + ",return body = "
//						+ EntityUtils.toString(response.getEntity()));
				// throw new IOException("request failed");
				return new R<>(String.valueOf(statusCode), EntityUtils.toString(response.getEntity()), StatusEnum.FAIL);
			}
		} catch (UnsupportedEncodingException e) {
			e.printStackTrace();
		} catch (ClientProtocolException e) {
			e.printStackTrace();
		} catch (ParseException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		}
		return new R<>(MsgCodeEnum.F_EXCEPTION_2);
	}

	public R<WxPayScoreRefundDTO> refund(WxPayRefundModel model, WxPayScoreConfig config) {
		ValidUtil.asserts(model);
		// 请求URL
		try {
			HttpPost httpPost = new HttpPost("https://api.mch.weixin.qq.com/v3/refund/domestic/refunds");
			String reqdata = model.toJson();
			if (log.isDebugEnabled()) {
				log.debug("微信支付分退款请求内容:{}", reqdata);
			}
			StringEntity entity = new StringEntity(reqdata, CharsetUtil.CHARSET_UTF_8);
			entity.setContentType("application/json");
			httpPost.setEntity(entity);
			httpPost.setHeader("Accept", "application/json");

			CloseableHttpClient httpClient = WxPayScoreConfigKit.getHttpClientCache(config);
			// 完成签名并执行请求
			CloseableHttpResponse response = httpClient.execute(httpPost);
			int statusCode = response.getStatusLine().getStatusCode();
			if (statusCode == 200) {
				String resultStr = EntityUtils.toString(response.getEntity());
				WxPayScoreRefundDTO parseObject = JSONObject.parseObject(resultStr, WxPayScoreRefundDTO.class);
				if (StrUtil.isBlank(parseObject.getStatus())) {
					return new R<>(MsgCodeEnum.FAIL, parseObject);
				} else if (StrUtil.equals("CLOSED", parseObject.getStatus()) || StrUtil.equals("ABNORMAL", parseObject.getStatus())) {
					return new R<>(MsgCodeEnum.FAIL, parseObject);
				}
				return new R<>(MsgCodeEnum.SUCCESS, parseObject);
			}
			return new R<>(String.valueOf(statusCode),EntityUtils.toString(response.getEntity()),StatusEnum.FAIL);
		} catch (ClientProtocolException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		return new R<>(MsgCodeEnum.F_EXCEPTION_2);
	}

	/**
	 * 解析回调信息
	 * 
	 * @param jsonEnc
	 * @param config
	 * @return
	 */
	public J<WxPayScoreNotifyPlainTextDTO,WxPayScoreCompleteDTO> notifyDecrypt(String jsonEnc, WxPayScoreConfig config) {
		WxPayScoreNotifyCiphertextDTO ciphertextDTO = JSONObject.parseObject(jsonEnc,
				WxPayScoreNotifyCiphertextDTO.class);
		String apiV3Key = config.getApiV3Key();

		WxPayNotifyResource resource = ciphertextDTO.getResource();
		byte[] associatedData = resource.getAssociated_data().getBytes(StandardCharsets.UTF_8);
		byte[] nonce = resource.getNonce().getBytes(StandardCharsets.UTF_8);
		String ciphertext = resource.getCiphertext();

		AesUtil aesUtil = new AesUtil(apiV3Key.getBytes(StandardCharsets.UTF_8));
		try {
			String plainText = aesUtil.decryptToString(associatedData, nonce, ciphertext);
			WxPayScoreCompleteDTO dto = JSONObject.parseObject(plainText, WxPayScoreCompleteDTO.class);
			return new J<>(MsgCodeEnum.SUCCESS, dto,new WxPayScoreNotifyPlainTextDTO(jsonEnc,plainText));
		} catch (GeneralSecurityException | IOException e) {
			e.printStackTrace();
		}
		return new J<>(MsgCodeEnum.F_EXCEPTION_2,null,new WxPayScoreNotifyPlainTextDTO(jsonEnc,null));
	}
}
